package fr.inria.arles.temperatureManagement.temperatureManager;

import com.sun.spot.io.j2me.radiogram.RadiogramConnection;
import javax.microedition.io.Connector;
import javax.microedition.io.Datagram;
import static fr.inria.arles.temperatureManagement.temperatureManager.SensorConstants.*;

/**
 * The server running at the processing node responsible for handling the
 * communication with the sensors and actuator.
 *
 * @author ajay chhatwal
 */
public class ProcessingNodeServer implements Runnable {

    /*
     * Permissible error in temperature.
     * If difference between actual and reference temperatures is more than this
     * then actuators are invoked to change the temperature.
     */
    private static final double PERMISSIBLE_ERROR = 1.0;
    /**
     * The reference temp.
     */
    private double referenceTemp = DEFAULT_TEMP_SETTING;
    /**
     * The Partition for which Processing Node is responsible.
     */
    private String myPartition;
    /**
     * The actual temp.
     */
    private double temp;

    public ProcessingNodeServer(String myPartition) {
        this.myPartition = myPartition;
    }

    private void actuate(short value) {
        RadiogramConnection rCon = null;
        Datagram dg = null;

        try {
            // Open up a broadcast connection to the host port
            // where the actuator is listening
            rCon = (RadiogramConnection) Connector.open("radiogram://broadcast:"
                    + ACTUATOR_HOST_PORT);
            dg = rCon.newDatagram(10);

            // Package the time and sensor reading into a radio datagram and send it.
            dg.reset();
            dg.writeShort(value);
            rCon.send(dg);

            System.out.println("Directing actuator to : " + value);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Listens to the datagrams sent by the sensor nodes and depending on the
     * average temperature and the reference temperature, directs the actuator
     * to heat or cool the room to bring it to the reference temperature.
     */
    @SuppressWarnings("static-access")
    public void run() {
        RadiogramConnection rCon;
        Datagram dg;

        try {
            // Open up a server-side broadcast radiogram connection
            // to listen for sensor readings being sent by different SPOTs
            rCon = (RadiogramConnection) Connector.open("radiogram://:"
                    + SENSOR_HOST_PORT);
            dg = rCon.newDatagram(rCon.getMaximumLength());
            double[] temps = new double[READING_HISTORY_LENGTH];
            // Main data collection loop

            for (int ctr = 0;; ctr = (ctr + 1) % READING_HISTORY_LENGTH) {


                // Read sensor sample received over the radio
                rCon.receive(dg);
                temps[ctr] = dg.readDouble();
                double sum = 0;
                for (double x : temps) {
                    sum += x;
                }
                temp = sum / READING_HISTORY_LENGTH;
                System.out.println("Average Temp:" + temp);

                double diff = referenceTemp - temp;
                if (Math.abs(diff) < PERMISSIBLE_ERROR) {
                    actuate(SWITCH_OFF);
                } else if (diff > 0) {
                    actuate(RAISE);
                } else {
                    actuate(LOWER);
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Gets the actual temperature of the partition managed by this
     * processing node.
     * @param partition The partition whose temperature is desired
     * @return the actual temperature of the partition
     * @throws IllegalArgumentException If the specified partition does not 
     * match the partition managed by this processing node
     */
    public String getTemp(String partition) throws IllegalArgumentException{
        if (partition.equals(myPartition)) {
            System.out.println("Get Temp:" + temp);
            return "" + temp;
        } else {
            throw new IllegalArgumentException("Wrong Partition");
        }
    }
    /**
     * Sets the reference temperature of the partition managed by this
     * processing node to the specified temperature.
     * @param partition The partition whose temperature is desired
     * @param temp The desired reference temperature
     * @throws IllegalArgumentException If the specified partition does not
     * match the partition managed by this processing node
     */
    public void setTemp(String partition, String temp) throws Exception {
        if (partition.equals(myPartition)) {
            referenceTemp = Double.parseDouble(temp);
            System.out.println("Temp set to " + temp);
        } else {
            throw new IllegalArgumentException("Wrong Partition");
        }
    }
}

